11. Strict Mode
默认情况下,Pydantic 会强制将值转化成期望的数据类型(For example, you can pass the string "123"
as the input to an int
field, and it will be converted to 123
),这种强制性的行为在很多场景下都是很有用的 — think: UUIDs, URL parameters, HTTP headers, environment variables, user input, etc.
不过,也有很多情况下,报错而不是强制转换是更为合适的行为
Pydantic 提供了 "strict mode" 来严格地检查数据类型,它可以应用在 model 级别、field 级别,甚至是单次验证级别。
from pydantic import BaseModel, ValidationError
class MyModel(BaseModel):
x: int
print(MyModel.model_validate({'x': '123'})) # lax mode
#> x=123
try:
MyModel.model_validate({'x': '123'}, strict=True) # strict mode
except ValidationError as exc:
print(exc)
"""
1 validation error for MyModel
x
Input should be a valid integer [type=int_type, input_value='123', input_type=str]
"""
Pydantic 提供了诸多方法来使用 strict-mode validation:
- 在验证方法中使用
strict=True
, such asBaseModel.model_validate
,TypeAdapter.validate_python
, and similar for JSON - 在字段定义时设置
Field(strict=True)
with fields of aBaseModel
,dataclass
, orTypedDict
- 使用
pydantic.types.Strict
作为类型标注 on a field - Pydantic 提供了一些自带严格模式的类型别名,such as
pydantic.types.StrictInt
- 使用
ConfigDict(strict=True)
strict mode 中的类型强制转换
对大部分类型而言,在严格模式下验证时,只有此类型下的实例才会被接受。For example, when validating an int
field, only instances of int
are accepted; passing instances of float
or str
will result in raising a ValidationError
.
不过从 JSON 中验证信息时,条件会更为宽松:For example, when validating a UUID
field, instances of str
will be accepted when validating from JSON, but not from python:
import json
from uuid import UUID
from pydantic import BaseModel, ValidationError
class MyModel(BaseModel):
guid: UUID
data = {'guid': '12345678-1234-1234-1234-123456789012'}
print(MyModel.model_validate(data)) # OK: lax
#> guid=UUID('12345678-1234-1234-1234-123456789012')
print(
MyModel.model_validate_json(json.dumps(data), strict=True)
) # OK: strict, but from json
#> guid=UUID('12345678-1234-1234-1234-123456789012')
try:
MyModel.model_validate(data, strict=True) # Not OK: strict, from python
except ValidationError as exc:
print(exc.errors(include_url=False))
"""
[
{
'type': 'is_instance_of',
'loc': ('guid',),
'msg': 'Input should be an instance of UUID',
'input': '12345678-1234-1234-1234-123456789012',
'ctx': {'class': 'UUID'},
}
]
"""
For more details about what types are allowed as inputs in strict mode, you can review the Conversion Table.
Strict mode in method calls
上述的所有例子都是通过在 validation methods 中传入 strict=True
来达成目的;你也可以在 TypeAdapter
传入这个参数,以期在验证单个值的时候启用严格模式:
from pydantic import TypeAdapter, ValidationError
print(TypeAdapter(bool).validate_python('yes')) # OK: lax
#> True
try:
TypeAdapter(bool).validate_python('yes', strict=True) # Not OK: strict
except ValidationError as exc:
print(exc)
"""
1 validation error for bool
Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
"""
更复杂的类型也不在话下:
from dataclasses import dataclass
from pydantic import TypeAdapter, ValidationError
@dataclass
class MyDataclass:
x: int
try:
TypeAdapter(MyDataclass).validate_python({'x': '123'}, strict=True)
except ValidationError as exc:
print(exc)
"""
1 validation error for MyDataclass
Input should be an instance of MyDataclass [type=dataclass_exact_type, input_value={'x': '123'}, input_type=dict]
"""
This also works with the TypeAdapter.validate_json
and BaseModel.model_validate_json
methods:
import json
from typing import List
from uuid import UUID
from pydantic import BaseModel, TypeAdapter, ValidationError
try:
TypeAdapter(List[int]).validate_json('["1", 2, "3"]', strict=True)
except ValidationError as exc:
print(exc)
"""
2 validation errors for list[int]
0
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
2
Input should be a valid integer [type=int_type, input_value='3', input_type=str]
"""
class Model(BaseModel):
x: int
y: UUID
data = {'x': '1', 'y': '12345678-1234-1234-1234-123456789012'}
try:
Model.model_validate(data, strict=True)
except ValidationError as exc:
# Neither x nor y are valid in strict mode from python:
print(exc)
"""
2 validation errors for Model
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
y
Input should be an instance of UUID [type=is_instance_of, input_value='12345678-1234-1234-1234-123456789012', input_type=str]
"""
json_data = json.dumps(data)
try:
Model.model_validate_json(json_data, strict=True)
except ValidationError as exc:
# From JSON, x is still not valid in strict mode, but y is:
print(exc)
"""
1 validation error for Model
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
Strict mode with Field
你可以 set strict=True
on the field. 这样做可以为此字段单独开启严格模式,即使 validation method 中没有 strict=True
from pydantic import BaseModel, Field, ValidationError
class User(BaseModel):
name: str
age: int
n_pets: int
user = User(name='John', age='42', n_pets='1')
print(user)
#> name='John' age=42 n_pets=1
class AnotherUser(BaseModel):
name: str
age: int = Field(strict=True)
n_pets: int
try:
anotheruser = AnotherUser(name='John', age='42', n_pets='1')
except ValidationError as e:
print(e)
"""
1 validation error for AnotherUser
age
Input should be a valid integer [type=int_type, input_value='42', input_type=str]
"""
Note that making fields strict will also affect the validation performed when instantiating the model class:
from pydantic import BaseModel, Field, ValidationError
class Model(BaseModel):
x: int = Field(strict=True)
y: int = Field(strict=False)
try:
Model(x='1', y='2')
except ValidationError as exc:
print(exc)
"""
1 validation error for Model
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
Using Field
as an annotation
Field(strict=True)
(或其他任何的关 键字参数) 可以在 Annotated
中使用。e.g., when working with TypedDict
:
from typing_extensions import Annotated, TypedDict
from pydantic import Field, TypeAdapter, ValidationError
class MyDict(TypedDict):
x: Annotated[int, Field(strict=True)]
try:
TypeAdapter(MyDict).validate_python({'x': '1'})
except ValidationError as exc:
print(exc)
"""
1 validation error for typed-dict
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
Strict mode with Annotated[..., Strict()]
Pydantic 还提供了 Strict
class, 用来作 [Annotated
] 的元数据; this annotation indicates that the annotated field should be validated in strict mode:
from typing_extensions import Annotated
from pydantic import BaseModel, Strict, ValidationError
class User(BaseModel):
name: str
age: int
is_active: Annotated[bool, Strict()]
User(name='David', age=33, is_active=True)
try:
User(name='David', age=33, is_active='True')
except ValidationError as exc:
print(exc)
"""
1 validation error for User
is_active
Input should be a valid boolean [type=bool_type, input_value='True', input_type=str]
"""
This is, in fact, the method used to implement some of the strict-out-of-the-box types provided by Pydantic, such as StrictInt
.